"""全局异常处理
"""
import logging
import inspect

from datetime import datetime
from zoneinfo import ZoneInfo
from functools import wraps
from collections import namedtuple
from typing import Callable, TypeVar

from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder

__all__ = ['AppException', 'HttpCode', 'HttpResp', 'unified_response']

from app.core.config import settings

logger = logging.getLogger(__name__)


RT = TypeVar('RT')  # 返回类型
HttpCode = namedtuple('HttpResp', ['code', 'msg'])


class HttpResp:
    """HTTP响应结果
    """
    SUCCESS = HttpCode(200, '成功')
    FAILED = HttpCode(300, '失败')
    PARAMS_VALID_ERROR = HttpCode(310, '参数校验错误')
    PARAMS_TYPE_ERROR = HttpCode(311, '参数类型错误')
    REQUEST_METHOD_ERROR = HttpCode(312, '请求方法错误')
    ASSERT_ARGUMENT_ERROR = HttpCode(313, '断言参数错误')

    LOGIN_ACCOUNT_ERROR = HttpCode(330, '登录账号或密码错误')
    LOGIN_DISABLE_ERROR = HttpCode(331, '登录账号已被禁用了')

    TOKEN_INVALID = HttpCode(401, 'token参数无效')
    NO_AUTH = HttpCode(403, '无相关权限')
    REQUEST_404_ERROR = HttpCode(404, '请求接口不存在')

    SYSTEM_ERROR = HttpCode(500, '系统错误')
    SYSTEM_TIMEOUT_ERROR = HttpCode(504, '请求超时')



class AppException(Exception):
    """应用异常基类
    """

    def __init__(self, exc: HttpCode, *args, code: int = None, msg: str = None, echo_exc: bool = False, **kwargs):
        super().__init__()
        _code = code if code is not None else exc.code
        _message = msg if msg is not None else exc.msg
        self._code = _code or HttpResp.FAILED.code
        self._message = _message or HttpResp.FAILED.msg
        self.echo_exc = echo_exc
        self.args = args or []
        self.kwargs = kwargs or {}

    @property
    def code(self) -> int:
        return self._code

    @property
    def msg(self) -> str:
        return self._message

    def __str__(self):
        return '{}: {}'.format(self.code, self.msg)



def format_datetime_fields(data):
    # 如果是 ORM 对象，转成 dict
    if hasattr(data, 'dict'):
        data = data.dict()

    if isinstance(data, dict):
        return {k: format_datetime_fields(v) for k, v in data.items()}
    elif isinstance(data, list):
        return [format_datetime_fields(item) for item in data]
    elif isinstance(data, datetime):
        return (
            # .astimezone(pytz.timezone(settings.TIMEZONE))
            data.replace(tzinfo=ZoneInfo("UTC")).strftime(settings.DATETIME_FORMAT)
        )
    else:
        return data


def unified_response(func: Callable[..., RT]) -> Callable[..., RT]:
    """统一响应格式
        接口正常返回时,统一响应结果格式
    """

    @wraps(func)
    async def wrapper(*args, **kwargs) -> RT:
        if inspect.iscoroutinefunction(func):
            resp = await func(*args, **kwargs) or []
        else:
            resp = func(*args, **kwargs) or []
        # 先格式化时间字段
        formatted_data = format_datetime_fields(resp)
        return JSONResponse(
            content=jsonable_encoder(
                # 正常请求响应
                {'code': HttpResp.SUCCESS.code, 'msg': HttpResp.SUCCESS.msg, 'data': formatted_data},
                by_alias=False,
                exclude_none=True
            ),
            media_type='application/json;charset=utf-8'
        )

    return wrapper